/**
 * 按分组列出账号以供选择
 */
import { CaretRightOutlined } from '@ant-design/icons'
import { truthy } from '@gnlab/utils'
import { Radio } from 'antd'
import c from 'classnames'
import { OrderedSet } from 'immutable'
import type { BaseAccount } from '@/services/auth'
import type { AccountGroup } from '@/services/features/accounts'
import type { AccountsData, AccountMap, ChoosingData } from '../hooks'
import './Groups.module.less'

export default function Groups(
  props: {
    accountsData: AccountsData
    multiple: boolean
  } & Pick<ChoosingData, 'locked' | 'choosed' | 'setChoosed'>,
) {
  const { accountsData, multiple, locked, choosed, setChoosed } = props
  return <div styleName="groups">
    {accountsData.groups.map((group, index) => <Group
      key={group.id}
      group={group}
      defaultOpen={index === 0}
      multiple={multiple}
      accountMap={accountsData.map}
      locked={locked}
      choosed={choosed}
      setChoosed={setChoosed}
    />)}
  </div>
}

enum GroupChoosing {
  None = 1,
  Partial = 2,
  Complete = 3,
}

function Group(
  props: {
    group: AccountGroup
    defaultOpen: boolean
    multiple: boolean
    accountMap: AccountMap
  } & Pick<ChoosingData, 'locked' | 'choosed' | 'setChoosed'>,
) {
  const { group, defaultOpen, multiple, accountMap, locked, choosed, setChoosed } = props

  const [opened, setOpened] = useState(defaultOpen)
  const switchOpen = () => setOpened(v => !v)

  // 判断账号是否被手动或固定选中
  function confirmChoosed(id: string) {
    return choosed.has(id) || locked.get(id) === true
  }

  // 此群组的“全选”状态
  let choosingStatus = GroupChoosing.None
  if (multiple) {
    // 仅多选情况下此状态才有意义
    const choosableCount = group.accountIds.filter(id => !locked.has(id)).length
    const choosedCount = group.accountIds.filter(confirmChoosed).length
    choosingStatus =
      choosedCount === 0
        ? GroupChoosing.None
        : choosedCount === choosableCount
        ? GroupChoosing.Complete
        : GroupChoosing.Partial
  }

  // 切换整个群组的“全选”状态
  function handleSwitchGroup(e: React.MouseEvent<HTMLElement>) {
    e.stopPropagation()
    if (!multiple) return

    let set = choosed
    group.accountIds.forEach(id => {
      set = locked.has(id)
        ? set
        : choosingStatus === GroupChoosing.Complete
        ? set.delete(id)
        : set.add(id)
    })
    setChoosed(set)
  }

  // 切换指定账号的选中状态
  function switchAccount(id: string) {
    if (locked.has(id)) return
    const set = choosed.has(id) ? choosed.delete(id) : multiple ? choosed.add(id) : OrderedSet([id])
    setChoosed(set)
  }

  const styleName = c(
    'group',
    multiple ? '' : 'single',
    opened ? 'opened' : '',
    choosingStatus === GroupChoosing.Partial ? 'partial' : '',
  )

  return <div styleName={styleName}>
    <header onClick={switchOpen}>
      {multiple ? (
        <Radio checked={choosingStatus !== GroupChoosing.None} onClick={handleSwitchGroup} />
      ) : null}
      <CaretRightOutlined styleName="collapse" />
      <span styleName="name">{group.name}</span>
    </header>

    {opened ? (
      <div styleName="accounts">
        {group.accountIds.map(id => <GroupAccount
          key={id}
          account={accountMap.get(id)}
          locked={locked.has(id)}
          choosed={confirmChoosed(id)}
          switchChoosed={() => switchAccount(id)}
        />)}
      </div>
    ) : null}
  </div>
}

function GroupAccount(props: {
  account?: BaseAccount
  locked: boolean
  choosed: boolean
  switchChoosed: () => void
}) {
  const { account, choosed, locked, switchChoosed } = props

  function handleSwitchChoose(e: React.MouseEvent<HTMLDivElement>) {
    e.preventDefault() // 屏蔽 Radio 的奇怪行为：点击白条部分时，会以 input 和 span 为 event.target 触发两次事件，导致无法正确修改状态
    if (!locked) switchChoosed()
  }

  return <div styleName="account" onClick={handleSwitchChoose}>
    <Radio checked={choosed} disabled={locked} />
    <span styleName="name">
      {account?.name ?? ''}
      {truthy(account?.remark) ? <span styleName="remark">（{account!.remark}）</span> : null}
    </span>
  </div>
}
